_Pragma("once")

#include <unordered_map>
#include <memory>

#include "log.hpp"

namespace sdl_engine::component::object_pool {

class GCObjectPool;

class GCObject {
public:
    friend class GCObjectPool;

    virtual ~GCObject() = default;

    /* @brief  get the index of this object,
     *         which point out the position in pool */
    inline int GetObjectIdx() const { return idx_; }
    inline const GCObjectPool * GetPool() const { return pool_; }

    /* @brief  set this object's `valid` flag false,
     *         you shouldn't use this object any more,
     *         this object will be free when it's pool GC */
    void DelSelf();

    /* @brief  immediately delete this object,
     *         both from pool and free it's memory */
    void ImDelSelf();

private:
    int idx_;
    bool valid_;
    GCObjectPool* pool_;
};


class GCObjectPool {
public:
    GCObjectPool(size_t reserve = 50);

    /* @brief  remove and free all objects from pool */
    inline void Clear() { pool_.clear(); }

    /* @brief  create an object from pool */
    template <typename T, typename ... Args>
    T* New(Args ... args) {
        int idx = findEmptySlot();
        T* rawObj = new T(args ...);
        std::unique_ptr<GCObject> obj(rawObj);
        if (!obj) {
            Loge("new object failed!");
            return nullptr;
        } else {
            obj->idx_ = idx;
            obj->valid_ = true;
            obj->pool_ = this;

            pool_[idx] = std::move(obj);
            return rawObj;
        }
    }

    inline int GetSize() const { return pool_.size(); }

    /* @brief  remove and free an object from pool */
    inline void Delete(GCObject* obj) {
        pool_.erase(obj->GetObjectIdx());
    }

    /* @brief  trigger a GC, it will remove all invalid object */
    void Gc();

    using PoolType = std::unordered_map<int, std::unique_ptr<GCObject>>;

private:
    PoolType pool_;

    int findEmptySlot();
    PoolType::iterator findInvalidSlot();
};

}
